package fox.framework.rpc.context;


import fox.framework.rpc.client.invocation.proxy.RpcProxy;
import fox.framework.rpc.common.StringUtils;
import fox.framework.rpc.context.definition.GenericRpcDefinition;
import fox.framework.rpc.context.definition.RpcDefinition;

import java.util.Collection;
import java.util.Collections;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author cuichao
 * @Description: MappedRegisteredRpcObjectFactory
 * @Date: create in 2021/1/28 15:54
 */
public class MappedRegisteredRpcObjectFactory extends ConfigurableRegisteredRpcObjectFactory implements RpcObjectFactory {


    private final Map<String, RpcDefinition> definitionObjectsCache = new ConcurrentHashMap<>(256);

    private final Map<String, Object> objectContainerCache = new ConcurrentHashMap<>(256);

    private final Map<String, RpcDefinition> definitionObjects = new ConcurrentHashMap<>(256);

    private final Map<String, Object> objectContainer = new ConcurrentHashMap<>(256);

    private final Map<Class<?>, String> objectNameByType = new ConcurrentHashMap<>(256);

    @Override
    public Object getObject(String name) {
        return objectContainer.getOrDefault(name, null);
    }

    @Override
    public Object getObject(Class clazz) {
        if (!objectNameByType.containsKey(clazz)) {
            return null;
        }
        return objectContainer.getOrDefault(objectNameByType.get(clazz), null);
    }

    @Override
    public void register(String name, RpcDefinition definition, Object object) {
        if (!definition.isProvider()) {
            throw new IllegalArgumentException("the method: register(String name, RpcDefinition definition, Object object) is register Rpc-Service object please check you code");
        }
        synchronized (definitionObjectsCache) {
            RpcDefinition existDef = definitionObjectsCache.getOrDefault(name, null);
            if (existDef == null) {
                definitionObjectsCache.put(name, definition);
                objectContainerCache.put(name, object);
                return;
            }
            if (existDef instanceof GenericRpcDefinition) {
                ((GenericRpcDefinition) existDef).setProvider(true);
            } else {

            }
            objectContainerCache.put(name, object);
            return;
        }
    }

    @Override
    public void register(String name, RpcDefinition definition) {
        if (definition.isProvider()) {
            throw new IllegalArgumentException("the method: register(String name, RpcDefinition definition) is register Rpc-Consumer object please check you code");
        }
        synchronized (definitionObjectsCache) {
            RpcDefinition existDef = definitionObjectsCache.getOrDefault(name, null);
            if (existDef == null) {
                definitionObjectsCache.put(name, definition);
            }
        }
        return;
    }

    @Override
    public boolean contain(String name) {
        if (StringUtils.isEmpty(name)) {
            return false;
        }
        return definitionObjects.containsKey(name) || definitionObjectsCache.containsKey(name);
    }

    @Override
    public RpcDefinition getDefinition(String name) {
        if (!definitionObjects.containsKey(name) && !definitionObjectsCache.containsKey(name)) {
            return null;
        }
        return definitionObjects.get(name) != null ? definitionObjects.get(name) : definitionObjectsCache.get(name);
    }

    @Override
    public Collection<RpcDefinition> getAllDefinitions() {
        Collection<RpcDefinition> definitions;

        if (!definitionObjects.values().isEmpty()) {
            definitions = definitionObjects.values();
        } else {
            definitions = definitionObjectsCache.values();
        }
        return Collections.unmodifiableCollection(definitions);
    }

    @Override
    protected void doRefresh() {
        //clear container
        definitionObjects.clear();
        objectContainer.clear();
        objectNameByType.clear();

        for (Map.Entry<String, RpcDefinition> entry : definitionObjectsCache.entrySet()) {
            String name = entry.getKey();
            RpcDefinition definition = entry.getValue();

            if (definition.isProvider()) {
                objectContainer.put(name, objectContainerCache.get(name));
            } else {
                //proxy instance
                RpcProxy proxy = getProxyFactory().getProxy(definition);
                Object object = proxy.getProxy();
                objectContainer.put(name, object);
            }

            definitionObjects.put(name, definition);
            objectNameByType.put(definition.serviceInterface(), name);
        }

        //clear cache
        definitionObjectsCache.clear();
        objectContainerCache.clear();

    }

}
